package es.gesfutbol.util;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.springframework.transaction.annotation.Transactional;

public class HibernateDao <T, TID extends Serializable> implements IHibernateDao<T, TID>{

    /**
     * Factoria de beans de la sessió
     */
    private SessionFactory sessionFactory;

    /**
     * Classe que es fa persistent
     */
    private final Class<T> persistentClass;

    /**
     * Constructor de la classe
     */
    @SuppressWarnings("unchecked")
    public HibernateDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * Retorna la classe persistent
     *
     * @return Classe persistent
     */
    public Class<T> getPersistentClass()
    {
        return persistentClass;
    }

    @Override
    public SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }

    @Override
    public void setSessionFactory(final SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    @Transactional
    @Override
    public void delete(final T objecte)
    {
        sessionFactory.getCurrentSession().delete(objecte);
    }

    @Transactional
    @Override
    public void store(final T objecte)
    {
        sessionFactory.getCurrentSession().saveOrUpdate(objecte);
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    @Override
    public T findById(final TID id)
    {
        return (T) sessionFactory.getCurrentSession().get(getPersistentClass(), id);
    }

    @Transactional(readOnly = true)
    @Override
    public List<T> findAll()
    {
        return findByCriteria();
    }

    /**
     * versió de <tt>findAll()</tt>, que retorna els resultats ordenats segons
     * un criteri d'ordenació especificat
     *
     * @see cat.base.baseframe.dao.IBaseDao#findAll(org.hibernate.criterion.Order)
     */
    @Transactional(readOnly = true)
    @Override
    public List<T> findAll(final Order orderPredicate)
    {
        return findByCriteria(orderPredicate);
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    protected List<T> findByCriteria(final Criterion... criterion)
    {
        final Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass());
        for(final Criterion c : criterion)
        {
            if(c != null)
            {
                crit.add(c);
            }
        }
        return crit.list();
    }

    /**
     * cerca sota un nombre variable de criteris de cerca, i ordena els
     * resultats segons un criteri d'ordenació.
     *
     * @param orderPredicate criteri d'ordenació
     * @param criterion criteris de cerca
     * @return la llista ordenada de resultats
     */
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    protected List<T> findByCriteria(final Order orderPredicate, final Criterion... criterion)
    {
        final Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass());
        for(final Criterion c : criterion)
        {
            if(c != null)
            {
                crit.add(c);
            }
        }
        return crit.addOrder(orderPredicate).list();
    }
}
